<?php


namespace LazadaApi\Services;


use Illuminate\Redis\RedisManager;
use Illuminate\Support\Facades\Log;
use LazadaApi\Exceptions\LazadaRefreshTokenExpiredException;

class LazadaAccessTokenService extends BaseLazadaService
{
    /**
     * 生成Oauth授权登录地址
     *
     * @param string $country 'sg,my,th,vn,ph,id,cb' :cb means auth all country.
     *
     * @param string $state   商户授权登录成功后由Lazada服务器原样返回到第三方指定的业务服务地址
     *
     * @return string
     */
    public function getOauthCodeUrl(string $country = 'cb', string $state = 'cb'): string
    {
        $callbackUrl = config('lazada.callbackUrl');

        return "https://auth.lazada.com/oauth/authorize?response_type=code&force_auth=true&redirect_uri={$callbackUrl}&client_id={$this->getAppKey()}&country={$country}&state={$state}";
    }

    /**
     * 生成 access_token [不需要 access_token]
     * 注意：返回的 refresh_token 通常是180天后过期，需要商户重新登录授权后才能获取新的refresh_token
     *
     * @see  https://open.lazada.com/doc/api.htm?spm=a2o9m.11193531.0.0.2c3d6bbe6GdZ0B#/api?cid=11&path=/auth/token/create
     *
     * @param string      $code oauth code, get from app callback URL.
     * @param string|null $uuid Unique identifier, anti-replay
     *
     * @return mixed
     * @throws \Exception
     */
    public function generate(string $code, string $uuid = null)
    {
        $this->addApiParam('code', $code);
        if (null !== $uuid) {
            $this->addApiParam('uuid', $uuid);
        }

        $accessTokenData = $this->post('/auth/token/create');
        $this->setAccessToken2Cache($accessTokenData);

        return $accessTokenData;
    }

    /**
     * 刷新 access_token [不需要 access_token]
     *
     * @see https://open.lazada.com/doc/api.htm?spm=a2o9m.11193531.0.0.2c3d6bbe6GdZ0B#/api?cid=11&path=/auth/token/refresh
     *
     * @param string $refreshToken 50001600212wcwiOabwyjtEH11acc19aBOvQr9ZYkYDlr987D8BB88LIB8bj
     *
     * @return mixed
     * @throws \Exception
     */
    public function refresh(string $refreshToken)
    {
        $this->addApiParam('refresh_token', $refreshToken);

        $accessTokenData = $this->post('/auth/token/refresh');

        $this->setAccessToken2Cache($accessTokenData);

        return $accessTokenData;
    }

    /**
     * 缓存 access_token 需要配置 lazada.isCacheToken = true
     *
     * @param array $lazadaTokenData
     *
     * @return bool
     */
    public function setAccessToken2Cache(array $lazadaTokenData): bool
    {
        if (!$this->_getIsCacheToken()) {
            return false;
        }
        $redisManager = $this->_getRedisManager();
        try {
            $countryUserInfos   = $lazadaTokenData['country_user_info'];
            $accessToken        = $lazadaTokenData['access_token'];
            $refreshToken       = $lazadaTokenData['refresh_token'];
            $expiresIn          = $lazadaTokenData['expires_in'];
            $refreshExpiresIn   = $lazadaTokenData['refresh_expires_in'];
            $minRefreshLeftTime = 1800;//  30 minutes before the token expires
            $expiresIn          -= $minRefreshLeftTime;
            $refreshExpiresIn   -= $minRefreshLeftTime;
            $redisManager->pipeline(function ($pipe) use ($countryUserInfos, $accessToken, $expiresIn, $refreshExpiresIn, $refreshToken) {
                foreach ($countryUserInfos as $countryUserInfo) {
                    $accessTokenKey = $this->_getAccessTokenKey($countryUserInfo['country'], $countryUserInfo['seller_id']);
                    $pipe->set($accessTokenKey, $accessToken);
                    $pipe->expire($accessTokenKey, $expiresIn);

                    $refreshTokenKey = $this->_getRefreshTokenKey($countryUserInfo['country'], $countryUserInfo['seller_id']);
                    $pipe->set($refreshTokenKey, $refreshToken);
                    $pipe->expire($refreshTokenKey, $refreshExpiresIn);
                }
            });

            return true;
        } catch (\Exception $e) {
            Log::error('设置Lazada AccessToken到缓存异常', ['msg' => $e->getMessage(), 'token_data' => $lazadaTokenData]);

            return false;
        }
    }

    /**
     * 从缓存中获取 access_token 需要配置 lazada.isCacheToken = true
     *
     * @param string $sellerId
     * @param string $country
     *
     * @return mixed|string
     * @throws \Exception
     */
    public function getAccessTokenBySellerId(string $sellerId, string $country)
    {
        if (!$this->_getIsCacheToken()) {
            throw new \Exception('Lazada Access Token 没有开启缓存，请开启缓存配置后重试', 500);
        }
        $redisManager   = $this->_getRedisManager();
        $accessTokenKey = $this->_getAccessTokenKey($country, $sellerId);
        $accessToken    = $redisManager->get($accessTokenKey);

        // AccessToken 过期
        if (empty($accessToken)) {
            $refreshTokenKey = $this->_getRefreshTokenKey($country, $sellerId);
            $refreshToken    = $redisManager->get($refreshTokenKey);

            // RefreshToken 过期
            if (empty($refreshToken)) {
                throw new LazadaRefreshTokenExpiredException('Lazada refresh token not found.');
            } else {
                $tokenData   = self::refresh($refreshToken);
                $accessToken = $tokenData['access_token'] ?? '';

                // RefreshToken 无法获取到新的AccessToken
                if (empty($accessToken)) {
                    throw new LazadaRefreshTokenExpiredException();
                }
            }
        }

        return $accessToken;
    }

    protected function getApiGateWay(): string
    {
        return self::API_AUTHORIZATION_URL;
    }

    private function _getIsCacheToken()
    {
        return config('lazada.isCacheToken', true);
    }

    private function _getRedisManager(): RedisManager
    {
        return app(RedisManager::class);
    }

    private function _getAccessTokenKey(string $country, $sellerId)
    {
        $country = strtolower(trim($country));

        return "lazada:access_token:${sellerId}:${country}";
    }

    private function _getRefreshTokenKey(string $country, $sellerId)
    {
        $country = strtolower(trim($country));

        return "lazada:refresh_token:${sellerId}:${country}";
    }

}